package org.login.akka;

import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.ReceiveBuilder;
import com.google.protobuf.ByteString;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.common.akka.actor.BaseActor;
import org.common.akka.message.*;
import org.common.dispatcher.ProtoDispatcher;
import org.common.netty.Pack;
import org.common.netty.PackCodec;
import org.common.utils.SpringUtils;
import org.login.defs.ConnectStatusEnum;
import org.login.obj.ConnectContext;
import org.protobuf.AccountMsg;
import org.protobuf.InnerServerMsg;
import org.protobuf.ProtoEnumMsg;

import java.lang.reflect.InvocationTargetException;

/**
 * 玩家的连接Actor
 */
@Slf4j
public class ConnectActor extends BaseActor {

    private final ConnectContext connectContext;


    public ConnectActor(ActorContext<BaseMsg> context, long connectId, Channel channel) {
        super(context);
        this.connectContext = new ConnectContext(connectId, channel);
    }

    /**
     * 生成一个ConnectActor
     * @return ConnectActor
     */
    public static Behavior<BaseMsg> create(long connectId, ChannelHandlerContext ctx) {
        return Behaviors.setup(context -> new ConnectActor(context, connectId, ctx.channel()));
    }

    @Override
    protected void onCreateReceive(ReceiveBuilder<BaseMsg> builder) {
        builder.onMessage(ClientUpMsg.class, this::onClientUpMsg);
        builder.onMessage(PushToClientMsg.class, this::onPushToClientMsg);
        builder.onMessage(ConnectClosedMsg.class, this::onConnectClosedMsg);
        builder.onMessage(RoleLoginMsg.class, this::onRoleLoginMsg);
    }

    /**
     * 角色在GameServer登录成功
     */
    private Behavior<BaseMsg> onRoleLoginMsg(RoleLoginMsg msg) {
        connectContext.setRoleId(msg.getRoleId());
        connectContext.setStatus(ConnectStatusEnum.ROLE_LOGIN);
        // 返回数据给客户端
        AccountMsg.S2CRoleLogin.Builder builder = AccountMsg.S2CRoleLogin.newBuilder();
        builder.setSuccess(true);
        connectContext.push(new Pack(ProtoEnumMsg.CMD.ID.ROLE_LOGIN_VALUE, builder));
        return this;
    }

    /**
     * 下行给客户端
     */
    private Behavior<BaseMsg> onPushToClientMsg(PushToClientMsg msg) {
        byte[] data = msg.getData();
        connectContext.getClientChannel().writeAndFlush(data);
        return this;
    }

    /**
     * 客户端上行数据
     */
    private Behavior<BaseMsg> onClientUpMsg(ClientUpMsg msg) throws InvocationTargetException, IllegalAccessException {
        Pack decode = PackCodec.decode(msg.getData());
        log.info("receive client up msg. cmdId = {}", decode.getCmdId());
        byte[] data = decode.getData();

        ProtoDispatcher dispatcher = SpringUtils.getBean(ProtoDispatcher.class);
        // 登录服不存在该协议的处理逻辑，则发送到游戏服处理
        if (!dispatcher.hasWorker(decode.getCmdId())) {
            dispatchProtoToGameServer(decode);
            return this;
        }
        Pack pack = dispatcher.dispatch(decode.getCmdId(), data, this.connectContext);
        if (pack != null) {
            this.connectContext.getClientChannel().writeAndFlush(PackCodec.encode(pack));
        }
        return this;
    }

    /**
     * 转发协议到GameServer
     */
    private void dispatchProtoToGameServer(Pack pack) {
        long roleId = connectContext.getRoleId();
        if (connectContext.getGameServerChannel() != null && roleId > 0) {
            InnerServerMsg.L2GClientUpMsg.Builder builder = InnerServerMsg.L2GClientUpMsg.newBuilder();
            builder.setRoleId(roleId);
            builder.setData(ByteString.copyFrom(pack.getData()));
            Pack dispatchPack = new Pack(ProtoEnumMsg.CMD.ID.CLIENT_PROTO_DISPATCH_VALUE, builder);
            connectContext.getGameServerChannel().writeAndFlush(PackCodec.encode(dispatchPack));
        }
    }

    /**
     * 连接关闭
     * 移除connectActor
     */
    private Behavior<BaseMsg> onConnectClosedMsg(ConnectClosedMsg msg) {
        log.info("receive connect closed msg.");
        ConnectActorManager connectActorManager = ConnectActorManager.getInstance();
        connectActorManager.removeConnectActor(this.connectContext.getConnectId());
        if (connectContext.getAccountId() > 0) {
            connectActorManager.removeConnectActorByAccountId(connectContext.getAccountId());
        }
        return this;
    }

}
